/**
 * 
 */
package co.edu.uniquindio.mundo;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.Scanner;

public class Retropropagacion {

	/**
	 * TODO PASO 7-15
	 */
	private double TASA_APRENDIZAJE = 0.02;
	private final static int MAX_ETAPAS = 1000;
	private final static double tolerancia = 0.01;
	private double[][] matrizV;
	private double[][] matrizW;
	private double[] vectorX;
	private double[] z_in;
	private int n;
	private int m;
	private int p;
	private double z[];
	private double[] y_in;
	private double[] y;
	private double d[];
	private double[] vectorObjetivos;
	private double[][] deltaW;
	private double[][] deltaV;
	private double[] c_in;// vector de errores
	private double[] c;// vector

	/**
	 * 
	 * @param n
	 * @param m
	 * @param p
	 */
	public Retropropagacion() {
		n = 0;
		m = 4;// usadas para el pro final
		p = 4;
		// m = 2;

		try {
			n = cargarPreguntasEntrenamiento(n, m, p);
//			System.out.println("N = " + n);
			matrizV = new double[n][p - 1];
			matrizW = new double[p][m];
			/*
			 * matrizV = new double[][] { { 0.45, -0.3, 0.1 }, { 0.05, -0.4,
			 * -0.2 }, { -0.32, 0.15, -0.1 }, { -0.25, -0.24, 0.3 }, { -0.5,
			 * -0.1, 0.5 }, { 0.1, -0.5, -0.3 } };
			 */

			/*
			 * matrizW = new double[][] { { -0.15, 0.1 }, { -0.01, 0.4 }, {
			 * 0.18, 0.02 }, { 0.3, -0.12 } };
			 */

			// matrizObjetivos = new double[][]{{0.8,-0.8}};

			inicializarMatriz(matrizV);
			inicializarMatriz(matrizW);

			// for (int i = 0; i < matrizW.length; i++) {
			// for (int j = 0; j < matrizW[0].length; j++) {
			// System.out.print(matrizW[i][j] + " ");
			// }
			// System.out.println();
			// }

			// iniciarEntrenamiento(n);

		} catch (Exception e) {

			e.printStackTrace();
		}
	}

	public void iniciarEntrenamiento() throws FileNotFoundException {
		Scanner lector;
		Scanner lectorObjetivos;
		etapas: for (int i = 0; i < MAX_ETAPAS; i++) {
			lector = new Scanner(new File(
					"src/co/edu/uniquindio/data/entrenamiento.txt"));
			lectorObjetivos = new Scanner(new File(
					"src/co/edu/uniquindio/data/objetivosEntrenamiento.txt"));

			while (lector.hasNext() && lectorObjetivos.hasNext()) {

				asignarEjemploEntrenamiento(n, lector);
				asignarObjetivosEjemploEntrenamiento(m, lectorObjetivos);
				z_in = multiplicarMatricesXV(vectorX, matrizV);
				// for (int j = 0; j < z_in.length; j++) {
				// System.out.print(z_in[j] + " ");
				//
				// }
				// System.out.println();
				z = calcularVectorZ(z_in);
				// System.out.print("Z = ");
				// for (int j = 0; j < z.length; j++) {
				// System.out.print(z[j] + " ");
				// }

				y_in = calcularYIN();
				// System.out.print("Y_in = ");
				// for (int j = 0; j < y_in.length; j++) {
				// System.out.print(y_in[j] + " ");
				// }
				// System.out.println();
				y = calcularVectorY();
				// System.out.print("Y = ");
				// for (int j = 0; j < y.length; j++) {
				// System.out.print(y[j] + " ");
				// }
				// System.out.println();

				d = calcularErrores();
				// for (int j = 0; j < d.length; j++) {
				// System.out.print(d[j] + " ");
				// }
				// System.out.println();

				// ahora hayamos DeltaW
				deltaW = calcularDeltaW();
				// System.out.println();
				// System.out.println("DeltaW : ");
				// for (int j = 0; j < deltaW.length; j++) {
				// for (int j2 = 0; j2 < deltaW[0].length; j2++) {
				// System.out.print(deltaW[j][j2] + " ");
				// }
				// System.out.println();
				// }

				// System.out.println("c_in");
				c_in = calcularVectorErrores();
				// for (int j = 0; j < c_in.length; j++) {
				// System.out.print(c_in[j] + " ");
				// }
				// System.out.println();
				c = calcularVectorC();
				// System.out.println("C = ");
				// for (int j = 0; j < c.length; j++) {
				// System.out.print(c[j] + " ");
				// }

				// System.out.println();
				// System.out.println("DeltaV = ");
				deltaV = calcularDeltaV();
				// for (int j = 0; j < deltaV.length; j++) {
				// for (int j2 = 0; j2 < deltaV[0].length; j2++) {
				// System.out.print(deltaV[j][j2] + " ");
				// }
				// System.out.println();
				// }

				asignarDeltas();

				// System.out.println();
				// System.out.println("Nuevos W y V");
				// for (int k = 0; k < matrizW.length; k++) {
				// for (int j = 0; j < matrizW[0].length; j++) {
				// System.out.print(matrizW[k][j]+" ");
				// }
				// System.out.println();
				// }
				// System.out.println();
				// System.out.println();
				// for (int k = 0; k < matrizV.length; k++) {
				// for (int j = 0; j < matrizV[0].length; j++) {
				// System.out.print(matrizV[k][j]+" ");
				// }
				// System.out.println();
				// }
				//
				//
				// break etapas;
			}
		}
	}

	private void asignarDeltas() {

		for (int i = 0; i < matrizW.length; i++) {
			for (int j = 0; j < matrizW[0].length; j++) {
				matrizW[i][j] += deltaW[i][j];
			}
		}

		for (int i = 0; i < matrizV.length; i++) {
			for (int j = 0; j < matrizV[0].length; j++) {
				matrizV[i][j] += deltaV[i][j];
			}
		}

	}

	private double[][] calcularDeltaV() {
		double[][] xtraspuesta = transponerArreglo(vectorX);
		double[][] solucion = new double[xtraspuesta.length][c.length];
		for (int i = 0; i < c.length; i++) {
			for (int j = 0; j < xtraspuesta.length; j++) {
				solucion[j][i] = TASA_APRENDIZAJE * xtraspuesta[j][0] * c[i];
			}
		}
		return solucion;

	}

	private double[] calcularVectorC() {
		double c[] = new double[c_in.length];
		for (int i = 0; i < c_in.length; i++) {
			c[i] = 0.5 * c_in[i] * (1 - (z[i + 1] * z[i + 1]));
		}
		return c;
	}

	private double[] calcularVectorErrores() {
		double[] solucion = new double[matrizW.length - 1];
		double[][] dTraspuesta = transponerArreglo(d);
		for (int i = 1; i < matrizW.length; i++) {
			for (int j = 0; j < dTraspuesta.length; j++) {
				solucion[i - 1] += dTraspuesta[j][0] * matrizW[i][j];
			}
		}
		return solucion;
	}

	private double[][] calcularDeltaW() {
		double[][] ztraspuesta = transponerArreglo(z);
		double[][] solucion = new double[ztraspuesta.length][d.length];
		for (int i = 0; i < d.length; i++) {
			for (int j = 0; j < ztraspuesta.length; j++) {
				solucion[j][i] = TASA_APRENDIZAJE * ztraspuesta[j][0] * d[i];
			}
		}
		return solucion;
	}

	private double[][] transponerArreglo(double[] arreglo) {
		double[][] arregloTranspuesto = new double[arreglo.length][1];

		for (int i = 0; i < arregloTranspuesto.length; i++) {
			arregloTranspuesto[i][0] = arreglo[i];
		}

		return arregloTranspuesto;
	}

	private void asignarObjetivosEjemploEntrenamiento(int m,
			Scanner lectorObjetivos) {
		vectorObjetivos = new double[m];
		for (int i = 0; i < vectorObjetivos.length; i++) {

			vectorObjetivos[i] = lectorObjetivos.nextDouble();
		}

	}

	private double[] calcularErrores() {
		double aux[] = new double[y.length];

		for (int i = 0; i < aux.length; i++) {
			aux[i] = 0.5 * ((vectorObjetivos[i] - y[i]) * (1 - (y[i] * y[i])));

		}
		return aux;
	}

	private double[] calcularVectorY() {
		double[] aux = new double[y_in.length];

		for (int i = 0; i < y_in.length; i++) {
			aux[i] = (2 / (1 + Math.exp(-y_in[i]))) - 1;
		}
		return aux;
	}

	private double[] calcularYIN() {
		double aux[] = new double[matrizW[0].length];

		for (int i = 0; i < aux.length; i++) {
			for (int j = 0; j < z.length; j++) {
				aux[i] += z[j] * matrizW[j][i];
			}
		}

		return aux;
	}

	private double[] calcularVectorZ(double[] z_in) {
		double[] aux = new double[z_in.length + 1];
		aux[0] = 1;
		for (int i = 0; i < z_in.length; i++) {
			aux[i + 1] = (2 / (1 + Math.exp(-z_in[i]))) - 1;
		}
		return aux;
	}

	private double[] multiplicarMatricesXV(double[] vectorX2,
			double[][] matrizV2) {

		double[] matrizAux = new double[p - 1];
//		System.out.println(vectorX2.length + "=" + matrizV2.length);
		for (int i = 0; i < matrizAux.length; i++) {
			for (int j = 0; j < vectorX2.length; j++) {
				matrizAux[i] += vectorX2[j] * matrizV2[j][i];
			}
		}

		return matrizAux;
	}

	private int cargarPreguntasEntrenamiento(int n, int m, int p)
			throws Exception {

		BufferedReader lector1 = new BufferedReader(new FileReader(new File(
				"src/co/edu/uniquindio/data/prueba.txt")));
		String linea = lector1.readLine();
		lector1.close();
		Scanner lector = new Scanner(linea);
		while (lector.hasNext()) {
			n++;
			lector.next();
		}
		lector.close();
		return n;

	}

	public void asignarEjemploEntrenamiento(int n, Scanner lector) {
		vectorX = new double[n];
		for (int i = 0; i < vectorX.length; i++) {
			vectorX[i] = lector.nextInt();
		}

	}

	/**
	 * 
	 * @param matriz
	 */
	public void inicializarMatriz(double[][] matriz) {
		for (int i = 0; i < matriz.length; i++) {
			for (int j = 0; j < matriz[0].length; j++) {
				double aleatorio = Math.random();
				if (aleatorio > 0.5)
					matriz[i][j] = aleatorio - 0.5;
				else
					matriz[i][j] = aleatorio * -1;
			}

		}

	}

	public double[] analizarValoresX(int[] x) {
		vectorX = new double[x.length];
		for (int i = 0; i < x.length; i++)
			vectorX[i] = x[i];
		// repetir pasos del 5 al 9
		// y retornar vector Y

		z_in = multiplicarMatricesXV(vectorX, matrizV);
		z = calcularVectorZ(z_in);
		y_in = calcularYIN();
		y = calcularVectorY();
		
		//TODO error al multiplicar X2 con V
		
		return y;
	}

}
